home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 151 / cd-rom 151.iso / internet / firefox / Firefox Setup 3.0 Beta 1.exe / nonlocalized / components / FeedWriter.js < prev    next >
Encoding:
Text File  |  2007-11-09  |  34.7 KB  |  982 lines

  1. //@line 41 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\feeds\src\FeedWriter.js"
  2.  
  3. const Cc = Components.classes;
  4. const Ci = Components.interfaces;
  5. const Cr = Components.results;
  6.  
  7. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  8.  
  9. function LOG(str) {
  10.   var prefB = Cc["@mozilla.org/preferences-service;1"].
  11.               getService(Ci.nsIPrefBranch);
  12.  
  13.   var shouldLog = false;
  14.   try {
  15.     shouldLog = prefB.getBoolPref("feeds.log");
  16.   } 
  17.   catch (ex) {
  18.   }
  19.  
  20.   if (shouldLog)
  21.     dump("*** Feeds: " + str + "\n");
  22. }
  23.  
  24. /**
  25.  * Wrapper function for nsIIOService::newURI.
  26.  * @param aURLSpec
  27.  *        The URL string from which to create an nsIURI.
  28.  * @returns an nsIURI object, or null if the creation of the URI failed.
  29.  */
  30. function makeURI(aURLSpec, aCharset) {
  31.   var ios = Cc["@mozilla.org/network/io-service;1"].
  32.             getService(Ci.nsIIOService);
  33.   try {
  34.     return ios.newURI(aURLSpec, aCharset, null);
  35.   } catch (ex) { }
  36.  
  37.   return null;
  38. }
  39.  
  40. const XML_NS = "http://www.w3.org/XML/1998/namespace"
  41. const HTML_NS = "http://www.w3.org/1999/xhtml";
  42. const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  43. const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
  44. const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";
  45.  
  46. const PREF_SELECTED_APP = "browser.feeds.handlers.application";
  47. const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
  48. const PREF_SELECTED_ACTION = "browser.feeds.handler";
  49. const PREF_SELECTED_READER = "browser.feeds.handler.default";
  50. const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
  51.  
  52. const TITLE_ID = "feedTitleText";
  53. const SUBTITLE_ID = "feedSubtitleText";
  54.  
  55. function FeedWriter() {}
  56. FeedWriter.prototype = {
  57.   _getPropertyAsBag: function FW__getPropertyAsBag(container, property) {
  58.     return container.fields.getProperty(property).
  59.                      QueryInterface(Ci.nsIPropertyBag2);
  60.   },
  61.  
  62.   _getPropertyAsString: function FW__getPropertyAsString(container, property) {
  63.     try {
  64.       return container.fields.getPropertyAsAString(property);
  65.     }
  66.     catch (e) {
  67.     }
  68.     return "";
  69.   },
  70.  
  71.   _setContentText: function FW__setContentText(id, text) {
  72.     var element = this._document.getElementById(id);
  73.     while (element.hasChildNodes())
  74.       element.removeChild(element.firstChild);
  75.     element.appendChild(this._document.createTextNode(text));
  76.   },
  77.  
  78.   /**
  79.    * Safely sets the href attribute on an anchor tag, providing the URI 
  80.    * specified can be loaded according to rules. 
  81.    * @param   element
  82.    *          The element to set a URI attribute on
  83.    * @param   attribute
  84.    *          The attribute of the element to set the URI to, e.g. href or src
  85.    * @param   uri
  86.    *          The URI spec to set as the href
  87.    */
  88.   _safeSetURIAttribute: 
  89.   function FW__safeSetURIAttribute(element, attribute, uri) {
  90.     var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
  91.                  getService(Ci.nsIScriptSecurityManager);    
  92.     const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
  93.     try {
  94.       secman.checkLoadURIStr(this._window.location.href, uri, flags);
  95.       // checkLoadURIStr will throw if the link URI should not be loaded per 
  96.       // the rules specified in |flags|, so we'll never "linkify" the link...
  97.       element.setAttribute(attribute, uri);
  98.     }
  99.     catch (e) {
  100.       // Not allowed to load this link because secman.checkLoadURIStr threw
  101.     }
  102.   },
  103.  
  104.   __faviconService: null,
  105.   get _faviconService() {
  106.     if (!this.__faviconService)
  107.       this.__faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
  108.                               getService(Ci.nsIFaviconService);
  109.  
  110.     return this.__faviconService;
  111.   },
  112.  
  113.   __bundle: null,
  114.   get _bundle() {
  115.     if (!this.__bundle) {
  116.       this.__bundle = Cc["@mozilla.org/intl/stringbundle;1"].
  117.                       getService(Ci.nsIStringBundleService).
  118.                       createBundle(URI_BUNDLE);
  119.     }
  120.     return this.__bundle;
  121.   },
  122.  
  123.   _getFormattedString: function FW__getFormattedString(key, params) {
  124.     return this._bundle.formatStringFromName(key, params, params.length);
  125.   },
  126.   
  127.   _getString: function FW__getString(key) {
  128.     return this._bundle.GetStringFromName(key);
  129.   },
  130.  
  131.   /* Magic helper methods to be used instead of xbl properties */
  132.   _getSelectedItemFromMenulist: function FW__getSelectedItemFromList(aList) {
  133.     var node = aList.firstChild.firstChild;
  134.     while (node) {
  135.       if (node.localName == "menuitem" && node.getAttribute("selected") == "true")
  136.         return node;
  137.  
  138.       node = node.nextSibling;
  139.     }
  140.  
  141.     return null;
  142.   },
  143.  
  144.   _setCheckboxCheckedState: function FW__setCheckboxCheckedState(aCheckbox, aValue) {
  145.     // see checkbox.xml
  146.     var change = (aValue != (aCheckbox.getAttribute('checked') == 'true'));
  147.     if (aValue)
  148.       aCheckbox.setAttribute('checked', 'true');
  149.     else
  150.       aCheckbox.removeAttribute('checked');
  151.  
  152.     if (change) {
  153.       var event = this._document.createEvent('Events');
  154.       event.initEvent('CheckboxStateChange', true, true);
  155.       aCheckbox.dispatchEvent(event);
  156.     }
  157.   },
  158.  
  159.   // For setting and getting the file expando property, we need to keep a
  160.   // reference to explict XPCNativeWrappers around the associated menuitems
  161.   _selectedApplicationItemWrapped: null,
  162.   get selectedApplicationItemWrapped() {
  163.     if (!this._selectedApplicationItemWrapped) {
  164.       this._selectedApplicationItemWrapped =
  165.         XPCNativeWrapper(this._document.getElementById("selectedAppMenuItem"));
  166.     }
  167.  
  168.     return this._selectedApplicationItemWrapped;
  169.   },
  170.  
  171.   _defaultSystemReaderItemWrapped: null,
  172.   get defaultSystemReaderItemWrapped() {
  173.     if (!this._defaultSystemReaderItemWrapped) {
  174.       // Unlike the selected application item, this might not exist at all,
  175.       // see _initSubscriptionUI
  176.       var menuItem = this._document.getElementById("defaultHandlerMenuItem");
  177.       if (menuItem)
  178.         this._defaultSystemReaderItemWrapped = XPCNativeWrapper(menuItem);
  179.     }
  180.  
  181.     return this._defaultSystemReaderItemWrapped;
  182.   },
  183.  
  184.    /**
  185.    * Returns a date suitable for displaying in the feed preview. 
  186.    * If the date cannot be parsed, the return value is "false".
  187.    * @param   dateString
  188.    *          A date as extracted from a feed entry. (entry.updated)
  189.    */
  190.   _parseDate: function FW__parseDate(dateString) {
  191.     // Convert the date into the user's local time zone
  192.     dateObj = new Date(dateString);
  193.  
  194.     // Make sure the date we're given is valid.
  195.     if (!dateObj.getTime())
  196.       return false;
  197.  
  198.     var dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
  199.                       getService(Ci.nsIScriptableDateFormat);
  200.     return dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatNoSeconds,
  201.                                       dateObj.getFullYear(), dateObj.getMonth()+1, dateObj.getDate(),
  202.                                       dateObj.getHours(), dateObj.getMinutes(), dateObj.getSeconds());
  203.   },
  204.  
  205.   /**
  206.    * Writes the feed title into the preview document.
  207.    * @param   container
  208.    *          The feed container
  209.    */
  210.   _setTitleText: function FW__setTitleText(container) {
  211.     if (container.title) {
  212.       this._setContentText(TITLE_ID, container.title.plainText());
  213.       this._document.title = container.title.plainText();
  214.     }
  215.  
  216.     var feed = container.QueryInterface(Ci.nsIFeed);
  217.     if (feed && feed.subtitle)
  218.       this._setContentText(SUBTITLE_ID, container.subtitle.plainText());
  219.   },
  220.  
  221.   /**
  222.    * Writes the title image into the preview document if one is present.
  223.    * @param   container
  224.    *          The feed container
  225.    */
  226.   _setTitleImage: function FW__setTitleImage(container) {
  227.     try {
  228.       var parts = container.image;
  229.       
  230.       // Set up the title image (supplied by the feed)
  231.       var feedTitleImage = this._document.getElementById("feedTitleImage");
  232.       this._safeSetURIAttribute(feedTitleImage, "src", 
  233.                                 parts.getPropertyAsAString("url"));
  234.  
  235.       // Set up the title image link
  236.       var feedTitleLink = this._document.getElementById("feedTitleLink");
  237.  
  238.       var titleText = this._getFormattedString("linkTitleTextFormat", 
  239.                                                [parts.getPropertyAsAString("title")]);
  240.       feedTitleLink.setAttribute("title", titleText);
  241.       this._safeSetURIAttribute(feedTitleLink, "href", 
  242.                                 parts.getPropertyAsAString("link"));
  243.  
  244.       // Fix the margin on the main title, so that the image doesn't run over
  245.       // the underline
  246.       var feedTitleText = this._document.getElementById("feedTitleText");
  247.       var titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;
  248.       feedTitleText.style.marginRight = titleImageWidth + "px";
  249.     }
  250.     catch (e) {
  251.       LOG("Failed to set Title Image (this is benign): " + e);
  252.     }
  253.   },
  254.  
  255.   /**
  256.    * Writes all entries contained in the feed.
  257.    * @param   container
  258.    *          The container of entries in the feed
  259.    */
  260.   _writeFeedContent: function FW__writeFeedContent(container) {
  261.     // Build the actual feed content
  262.     var feedContent = this._document.getElementById("feedContent");
  263.     var feed = container.QueryInterface(Ci.nsIFeed);
  264.     
  265.     for (var i = 0; i < feed.items.length; ++i) {
  266.       var entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
  267.       entry.QueryInterface(Ci.nsIFeedContainer);
  268.       
  269.       var entryContainer = this._document.createElementNS(HTML_NS, "div");
  270.       entryContainer.className = "entry";
  271.  
  272.       // If the entry has a title, make it a link
  273.       if (entry.title) {
  274.         var a = this._document.createElementNS(HTML_NS, "a");
  275.         a.appendChild(this._document.createTextNode(entry.title.plainText()));
  276.       
  277.         // Entries are not required to have links, so entry.link can be null.
  278.         if (entry.link)
  279.           this._safeSetURIAttribute(a, "href", entry.link.spec);
  280.  
  281.         var title = this._document.createElementNS(HTML_NS, "h3");
  282.         title.appendChild(a);
  283.         entryContainer.appendChild(title);
  284.  
  285.         var lastUpdated = this._parseDate(entry.updated);
  286.         if (lastUpdated) {
  287.           var dateDiv = this._document.createElementNS(HTML_NS, "div");
  288.           dateDiv.setAttribute("class", "lastUpdated");
  289.           title.appendChild(dateDiv);
  290.           dateDiv.textContent = lastUpdated;
  291.         }
  292.       }
  293.  
  294.       var body = this._document.createElementNS(HTML_NS, "div");
  295.       var summary = entry.summary || entry.content;
  296.       var docFragment = null;
  297.       if (summary) {
  298.  
  299.         if (summary.base)
  300.           body.setAttributeNS(XML_NS, "base", summary.base.spec);
  301.         else
  302.           LOG("no base?");
  303.         docFragment = summary.createDocumentFragment(body);
  304.         if (docFragment)
  305.           body.appendChild(docFragment);
  306.  
  307.         // If the entry doesn't have a title, append a # permalink
  308.         // See http://scripting.com/rss.xml for an example
  309.         if (!entry.title && entry.link) {
  310.           var a = this._document.createElementNS(HTML_NS, "a");
  311.           a.appendChild(this._document.createTextNode("#"));
  312.           this._safeSetURIAttribute(a, "href", entry.link.spec);
  313.           body.appendChild(this._document.createTextNode(" "));
  314.           body.appendChild(a);
  315.         }
  316.  
  317.       }
  318.       body.className = "feedEntryContent";
  319.       entryContainer.appendChild(body);
  320.       feedContent.appendChild(entryContainer);
  321.       var clearDiv = this._document.createElementNS(HTML_NS, "div");
  322.       clearDiv.style.clear = "both";
  323.       feedContent.appendChild(clearDiv);
  324.     }
  325.   },
  326.  
  327.   /**
  328.    * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
  329.    * Displays error information if there was one.
  330.    * @param   result
  331.    *          The parsed feed result
  332.    * @returns A valid nsIFeedContainer object containing the contents of
  333.    *          the feed.
  334.    */
  335.   _getContainer: function FW__getContainer(result) {
  336.     var feedService = 
  337.         Cc["@mozilla.org/browser/feeds/result-service;1"].
  338.         getService(Ci.nsIFeedResultService);
  339.  
  340.     try {
  341.       var result = 
  342.         feedService.getFeedResult(this._getOriginalURI(this._window));
  343.     }
  344.     catch (e) {
  345.       LOG("Subscribe Preview: feed not available?!");
  346.     }
  347.     
  348.     if (result.bozo) {
  349.       LOG("Subscribe Preview: feed result is bozo?!");
  350.     }
  351.  
  352.     try {
  353.       var container = result.doc;
  354.     }
  355.     catch (e) {
  356.       LOG("Subscribe Preview: no result.doc? Why didn't the original reload?");
  357.       return null;
  358.     }
  359.     return container;
  360.   },
  361.  
  362.   /**
  363.    * Get the human-readable display name of a file. This could be the 
  364.    * application name.
  365.    * @param   file
  366.    *          A nsIFile to look up the name of
  367.    * @returns The display name of the application represented by the file.
  368.    */
  369.   _getFileDisplayName: function FW__getFileDisplayName(file) {
  370. //@line 410 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\feeds\src\FeedWriter.js"
  371.     if (file instanceof Ci.nsILocalFileWin) {
  372.       try {
  373.         return file.getVersionInfoField("FileDescription");
  374.       }
  375.       catch (e) {
  376.       }
  377.     }
  378. //@line 427 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\feeds\src\FeedWriter.js"
  379.     var ios = 
  380.         Cc["@mozilla.org/network/io-service;1"].
  381.         getService(Ci.nsIIOService);
  382.     var url = ios.newFileURI(file).QueryInterface(Ci.nsIURL);
  383.     return url.fileName;
  384.   },
  385.  
  386.   /**
  387.    * Get moz-icon url for a file
  388.    * @param   file
  389.    *          A nsIFile object for which the moz-icon:// is returned
  390.    * @returns moz-icon url of the given file as a string
  391.    */
  392.   _getFileIconURL: function FW__getFileIconURL(file) {
  393.     var ios = Cc["@mozilla.org/network/io-service;1"].
  394.               getService(Components.interfaces.nsIIOService);
  395.     var fph = ios.getProtocolHandler("file")
  396.                  .QueryInterface(Ci.nsIFileProtocolHandler);
  397.     var urlSpec = fph.getURLSpecFromFile(file);
  398.     return "moz-icon://" + urlSpec + "?size=16";
  399.   },
  400.  
  401.   /**
  402.    * Helper method to set the selected application and system default
  403.    * reader menuitems details from a file object
  404.    *   @param aMenuItem
  405.    *          The menuitem on which the attributes should be set
  406.    *   @param aFile
  407.    *          The menuitem's associated file
  408.    */
  409.   _initMenuItemWithFile: function(aMenuItem, aFile) {
  410.     aMenuItem.setAttribute("label", this._getFileDisplayName(aFile));
  411.     aMenuItem.setAttribute("image", this._getFileIconURL(aFile));
  412.     aMenuItem.file = aFile;
  413.   },
  414.  
  415.   /**
  416.    * Displays a prompt from which the user may choose a (client) feed reader.
  417.    * @return - true if a feed reader was selected, false otherwise.
  418.    */
  419.   _chooseClientApp: function FW__chooseClientApp() {
  420.     try {
  421.       var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  422.       fp.init(this._window,
  423.               this._getString("chooseApplicationDialogTitle"),
  424.               Ci.nsIFilePicker.modeOpen);
  425.       fp.appendFilters(Ci.nsIFilePicker.filterApps);
  426.  
  427.       if (fp.show() == Ci.nsIFilePicker.returnOK) {
  428.         var selectedApp = fp.file;
  429.         if (selectedApp) {
  430.           // XXXben - we need to compare this with the running instance executable
  431.           //          just don't know how to do that via script...
  432.           // XXXmano TBD: can probably add this to nsIShellService
  433. //@line 482 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\feeds\src\FeedWriter.js"
  434.           if (fp.file.leafName != "firefox.exe") {
  435. //@line 490 "e:\builds\tinderbox\Fx-Rel\WINNT_5.2_Depend\mozilla\browser\components\feeds\src\FeedWriter.js"
  436.             var selectedAppMenuItem = this.selectedApplicationItemWrapped;
  437.             this._initMenuItemWithFile(selectedAppMenuItem, selectedApp);
  438.  
  439.             // Show and select the selected application menuitem
  440.             selectedAppMenuItem.hidden = false;
  441.             selectedAppMenuItem.doCommand();
  442.             return true;
  443.           }
  444.         }
  445.       }
  446.     }
  447.     catch(ex) { }
  448.  
  449.     return false;
  450.   },
  451.  
  452.   _setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState() {
  453.     var checkbox = this._document.getElementById("alwaysUse");
  454.     if (checkbox) {
  455.       var alwaysUse = false;
  456.       try {
  457.         var prefs = Cc["@mozilla.org/preferences-service;1"].
  458.                     getService(Ci.nsIPrefBranch);
  459.         if (prefs.getCharPref(PREF_SELECTED_ACTION) != "ask")
  460.           alwaysUse = true;
  461.       }
  462.       catch(ex) { }
  463.       this._setCheckboxCheckedState(checkbox, alwaysUse);
  464.     }
  465.   },
  466.  
  467.   _setAlwaysUseLabel: function FW__setAlwaysUseLabel() {
  468.     var checkbox = this._document.getElementById("alwaysUse");
  469.     if (checkbox) {
  470.       var handlersMenuList = this._document.getElementById("handlersMenuList");
  471.       if (handlersMenuList) {
  472.         var handlerName = this._getSelectedItemFromMenulist(handlersMenuList)
  473.                               .getAttribute("label");
  474.         checkbox.setAttribute("label", this._getFormattedString("alwaysUse", [handlerName]));
  475.       }
  476.     }
  477.   },
  478.  
  479.   // nsIDomEventListener
  480.   handleEvent: function(event) {
  481.     // see comments in the write method
  482.     event = new XPCNativeWrapper(event);
  483.     if (event.target.ownerDocument != this._document) {
  484.       LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
  485.       return;
  486.     }
  487.  
  488.     if (event.type == "command") {
  489.       switch (event.target.id) {
  490.         case "subscribeButton":
  491.           this.subscribe();
  492.           break;
  493.         case "chooseApplicationMenuItem":
  494.           /* Bug 351263: Make sure to not steal focus if the "Choose
  495.            * Application" item is being selected with the keyboard. We do this
  496.            * by ignoring command events while the dropdown is closed (user
  497.            * arrowing through the combobox), but handling them while the
  498.            * combobox dropdown is open (user pressed enter when an item was
  499.            * selected). If we don't show the filepicker here, it will be shown
  500.            * when clicking "Subscribe Now".
  501.            */
  502.           var popupbox = this._document.getElementById("handlersMenuList")
  503.                              .firstChild.boxObject;
  504.           popupbox.QueryInterface(Components.interfaces.nsIPopupBoxObject);
  505.           if (popupbox.popupState == "hiding" && !this._chooseClientApp()) {
  506.             // Select the (per-prefs) selected handler if no application was
  507.             // selected
  508.             this._setSelectedHandler();
  509.           }
  510.           break;
  511.         default:
  512.           this._setAlwaysUseLabel();
  513.       }
  514.     }
  515.   },
  516.  
  517.   _setSelectedHandler: function FW__setSelectedHandler() {
  518.     var prefs =   
  519.         Cc["@mozilla.org/preferences-service;1"].
  520.         getService(Ci.nsIPrefBranch);
  521.  
  522.     var handler = "bookmarks";
  523.     try {
  524.       handler = prefs.getCharPref(PREF_SELECTED_READER);
  525.     }
  526.     catch (ex) { }
  527.  
  528.     switch (handler) {
  529.       case "web": {
  530.         var handlersMenuList = this._document.getElementById("handlersMenuList");
  531.         if (handlersMenuList) {
  532.           var url = prefs.getComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString).data;
  533.           var handlers =
  534.             handlersMenuList.getElementsByAttribute("webhandlerurl", url);
  535.           if (handlers.length == 0) {
  536.             LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist")
  537.             return;
  538.           }
  539.  
  540.           handlers[0].doCommand();
  541.         }
  542.         break;
  543.       }
  544.       case "client": {
  545.         var selectedAppMenuItem = this.selectedApplicationItemWrapped;
  546.         if (selectedAppMenuItem) {
  547.           try {
  548.             var selectedApp = prefs.getComplexValue(PREF_SELECTED_APP,
  549.                                                     Ci.nsILocalFile);
  550.           } catch(ex) { }
  551.  
  552.           if (selectedApp) {
  553.             this._initMenuItemWithFile(selectedAppMenuItem, selectedApp);
  554.             selectedAppMenuItem.hidden = false;
  555.             selectedAppMenuItem.doCommand();
  556.  
  557.             // Only show the default reader menuitem if the default reader
  558.             // isn't the selected application
  559.             var defaultHandlerMenuItem = this.defaultSystemReaderItemWrapped;
  560.             if (defaultHandlerMenuItem) {
  561.               defaultHandlerMenuItem.hidden =
  562.                 defaultHandlerMenuItem.file.path == selectedApp.path;
  563.             }
  564.             break;
  565.           }
  566.         }
  567.       }
  568.       case "bookmarks":
  569.       default: {
  570.         var liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");
  571.         if (liveBookmarksMenuItem)
  572.           liveBookmarksMenuItem.doCommand();
  573.       } 
  574.     }
  575.   },
  576.  
  577.   _initSubscriptionUI: function FW__initSubscriptionUI() {
  578.     var handlersMenuPopup = this._document.getElementById("handlersMenuPopup");
  579.     if (!handlersMenuPopup)
  580.       return;
  581.  
  582.     // Last-selected application
  583.     var selectedApp;
  584.     var menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  585.     menuItem.id = "selectedAppMenuItem";
  586.     menuItem.className = "menuitem-iconic";
  587.     menuItem.setAttribute("handlerType", "client");
  588.     handlersMenuPopup.appendChild(menuItem);
  589.  
  590.     var selectedApplicationItem = this.selectedApplicationItemWrapped;
  591.     try {
  592.       var prefs = Cc["@mozilla.org/preferences-service;1"].
  593.                   getService(Ci.nsIPrefBranch);
  594.       selectedApp = prefs.getComplexValue(PREF_SELECTED_APP,
  595.                                           Ci.nsILocalFile);
  596.  
  597.       if (selectedApp.exists())
  598.         this._initMenuItemWithFile(selectedApplicationItem, selectedApp);
  599.       else {
  600.         // Hide the menuitem if the last selected application doesn't exist
  601.         selectedApplicationItem.hidden = true;
  602.       }
  603.     }
  604.     catch(ex) {
  605.       // Hide the menuitem until an application is selected
  606.       selectedApplicationItem.hidden = true;
  607.     }
  608.  
  609.     // List the default feed reader
  610.     var defaultReader = null;
  611.     try {
  612.       var defaultReader = Cc["@mozilla.org/browser/shell-service;1"].
  613.                           getService(Ci.nsIShellService).defaultFeedReader;
  614.       menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  615.       menuItem.id = "defaultHandlerMenuItem";
  616.       menuItem.className = "menuitem-iconic";
  617.       menuItem.setAttribute("handlerType", "client");
  618.       handlersMenuPopup.appendChild(menuItem);
  619.  
  620.       var defaultSystemReaderItem = this.defaultSystemReaderItemWrapped;
  621.       this._initMenuItemWithFile(defaultSystemReaderItem, defaultReader);
  622.  
  623.       // Hide the default reader item if it points to the same application
  624.       // as the last-selected application
  625.       if (selectedApp && selectedApp.path == defaultReader.path)
  626.         defaultSystemReaderItem.hidden = true;
  627.     }
  628.     catch(ex) { /* no default reader */ }
  629.  
  630.     // "Choose Application..." menuitem
  631.     menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  632.     menuItem.id = "chooseApplicationMenuItem";
  633.     menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem"));
  634.     handlersMenuPopup.appendChild(menuItem);
  635.  
  636.     // separator
  637.     handlersMenuPopup.appendChild(this._document.createElementNS(XUL_NS,
  638.                                   "menuseparator"));
  639.  
  640.     var historySvc = Cc["@mozilla.org/browser/nav-history-service;1"].
  641.                      getService(Ci.nsINavHistoryService);
  642.     historySvc.addObserver(this, false);
  643.  
  644.     // List of web handlers
  645.     var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
  646.                getService(Ci.nsIWebContentConverterService);
  647.     var handlers = wccr.getContentHandlers(TYPE_MAYBE_FEED, {});
  648.     if (handlers.length != 0) {
  649.       for (var i = 0; i < handlers.length; ++i) {
  650.         menuItem = this._document.createElementNS(XUL_NS, "menuitem");
  651.         menuItem.className = "menuitem-iconic";
  652.         menuItem.setAttribute("label", handlers[i].name);
  653.         menuItem.setAttribute("handlerType", "web");
  654.         menuItem.setAttribute("webhandlerurl", handlers[i].uri);
  655.         handlersMenuPopup.appendChild(menuItem);
  656.  
  657.         // For privacy reasons we cannot set the image attribute directly
  658.         // to the icon url, see Bug 358878
  659.         var uri = makeURI(handlers[i].uri);
  660.         if (!this._setFaviconForWebReader(uri, menuItem)) {
  661.           if (uri && /^https?/.test(uri.scheme)) {
  662.             var iconURL = makeURI(uri.prePath + "/favicon.ico");
  663.             this._faviconService.setAndLoadFaviconForPage(uri, iconURL, true);
  664.           }
  665.         }
  666.       }
  667.     }
  668.  
  669.     this._setSelectedHandler();
  670.  
  671.     // "Always use..." checkbox initial state
  672.     this._setAlwaysUseCheckedState();
  673.     this._setAlwaysUseLabel();
  674.  
  675.     // We update the "Always use.." checkbox label whenever the selected item
  676.     // in the list is changed
  677.     handlersMenuPopup.addEventListener("command", this, false);
  678.  
  679.     // Set up the "Subscribe Now" button
  680.     this._document
  681.         .getElementById("subscribeButton")
  682.         .addEventListener("command", this, false);
  683.  
  684.     // first-run ui
  685.     var showFirstRunUI = true;
  686.     try {
  687.       showFirstRunUI = prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
  688.     }
  689.     catch (ex) { }
  690.     if (showFirstRunUI) {
  691.       var feedHeader = this._document.getElementById("feedHeader");
  692.       if (feedHeader)
  693.         feedHeader.setAttribute("firstrun", "true");
  694.  
  695.       prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
  696.     }
  697.   },
  698.  
  699.   /**
  700.    * Returns the original URI object of the feed and ensures that this
  701.    * component is only ever invoked from the preview document.  
  702.    * @param aWindow 
  703.    *        The window of the document invoking the BrowserFeedWriter
  704.    */
  705.   _getOriginalURI: function FW__getOriginalURI(aWindow) {
  706.     var chan = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
  707.                getInterface(Ci.nsIWebNavigation).
  708.                QueryInterface(Ci.nsIDocShell).currentDocumentChannel;
  709.  
  710.     const SUBSCRIBE_PAGE_URI = "chrome://browser/content/feeds/subscribe.xhtml";
  711.     var uri = makeURI(SUBSCRIBE_PAGE_URI);
  712.     var resolvedURI = Cc["@mozilla.org/chrome/chrome-registry;1"].
  713.                       getService(Ci.nsIChromeRegistry).
  714.                       convertChromeURL(uri);
  715.  
  716.     if (resolvedURI.equals(chan.URI))
  717.       return chan.originalURI;
  718.  
  719.     return null;
  720.   },
  721.  
  722.   _window: null,
  723.   _document: null,
  724.   _feedURI: null,
  725.  
  726.   // nsIFeedWriter
  727.   init: function FW_init(aWindow) {
  728.     // Explicitly wrap |window| in an XPCNativeWrapper to make sure
  729.     // it's a real native object! This will throw an exception if we
  730.     // get a non-native object.
  731.     var window = new XPCNativeWrapper(aWindow);
  732.     this._feedURI = this._getOriginalURI(window);
  733.     if (!this._feedURI)
  734.       return;
  735.  
  736.     this._window = window;
  737.     this._document = window.document;
  738.  
  739.     LOG("Subscribe Preview: feed uri = " + this._window.location.href);
  740.  
  741.     // Set up the subscription UI
  742.     this._initSubscriptionUI();
  743.     var prefs = Cc["@mozilla.org/preferences-service;1"].
  744.                 getService(Ci.nsIPrefBranch2);
  745.     prefs.addObserver(PREF_SELECTED_ACTION, this, false);
  746.     prefs.addObserver(PREF_SELECTED_READER, this, false);
  747.     prefs.addObserver(PREF_SELECTED_WEB, this, false);
  748.     prefs.addObserver(PREF_SELECTED_APP, this, false);
  749.   },
  750.  
  751.   writeContent: function FW_writeContent() {
  752.     if (!this._window)
  753.       return;
  754.  
  755.     try {
  756.       // Set up the feed content
  757.       var container = this._getContainer();
  758.       if (!container)
  759.         return;
  760.  
  761.       this._setTitleText(container);
  762.       this._setTitleImage(container);
  763.       this._writeFeedContent(container);
  764.     }
  765.     finally {
  766.       this._removeFeedFromCache();
  767.     }
  768.   },
  769.  
  770.   close: function FW_close() {
  771.     this._document
  772.         .getElementById("handlersMenuPopup")
  773.         .removeEventListener("command", this, false);
  774.     this._document
  775.         .getElementById("subscribeButton")
  776.         .removeEventListener("command", this, false);
  777.     this._document = null;
  778.     this._window = null;
  779.     var prefs = Cc["@mozilla.org/preferences-service;1"].
  780.                 getService(Ci.nsIPrefBranch2);
  781.     prefs.removeObserver(PREF_SELECTED_ACTION, this);
  782.     prefs.removeObserver(PREF_SELECTED_READER, this);
  783.     prefs.removeObserver(PREF_SELECTED_WEB, this);
  784.     prefs.removeObserver(PREF_SELECTED_APP, this);
  785.     this._removeFeedFromCache();
  786.     this.__faviconService = null;
  787.     this.__bundle = null;
  788.     this._selectedApplicationItemWrapped = null;
  789.     this._defaultSystemReaderItemWrapped = null;
  790.     this._FeedURI = null;
  791.     var historySvc = Cc["@mozilla.org/browser/nav-history-service;1"].
  792.                      getService(Ci.nsINavHistoryService);
  793.     historySvc.removeObserver(this);
  794.   },
  795.  
  796.   _removeFeedFromCache: function FW__removeFeedFromCache() {
  797.     if (this._feedURI) {
  798.       var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
  799.                         getService(Ci.nsIFeedResultService);
  800.       feedService.removeFeedResult(this._feedURI);
  801.       this._feedURI = null;
  802.     }
  803.   },
  804.  
  805.   subscribe: function FW_subscribe() {
  806.     // Subscribe to the feed using the selected handler and save prefs
  807.     var prefs = Cc["@mozilla.org/preferences-service;1"].
  808.                 getService(Ci.nsIPrefBranch);
  809.     var defaultHandler = "reader";
  810.     var useAsDefault = this._document.getElementById("alwaysUse")
  811.                                      .getAttribute("checked");
  812.  
  813.     var handlersMenuList = this._document.getElementById("handlersMenuList");
  814.     var selectedItem = this._getSelectedItemFromMenulist(handlersMenuList);
  815.  
  816.     // Show the file picker before subscribing if the
  817.     // choose application menuitem was choosen using the keyboard
  818.     if (selectedItem.id == "chooseApplicationMenuItem") {
  819.       if (!this._chooseClientApp())
  820.         return;
  821.       
  822.       selectedItem = this._getSelectedItemFromMenulist(handlersMenuList);
  823.     }
  824.  
  825.     if (selectedItem.hasAttribute("webhandlerurl")) {
  826.       var webURI = selectedItem.getAttribute("webhandlerurl");
  827.       prefs.setCharPref(PREF_SELECTED_READER, "web");
  828.  
  829.       var supportsString = Cc["@mozilla.org/supports-string;1"].
  830.                            createInstance(Ci.nsISupportsString);
  831.       supportsString.data = webURI;
  832.       prefs.setComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString,
  833.                             supportsString);
  834.  
  835.       var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
  836.                  getService(Ci.nsIWebContentConverterService);
  837.       var handler = wccr.getWebContentHandlerByURI(TYPE_MAYBE_FEED, webURI);
  838.       if (handler) {
  839.         if (useAsDefault)
  840.           wccr.setAutoHandler(TYPE_MAYBE_FEED, handler);
  841.  
  842.         this._window.location.href = handler.getHandlerURI(this._window.location.href);
  843.       }
  844.     }
  845.     else {
  846.       switch (selectedItem.id) {
  847.         case "selectedAppMenuItem":
  848.           prefs.setCharPref(PREF_SELECTED_READER, "client");
  849.           prefs.setComplexValue(PREF_SELECTED_APP, Ci.nsILocalFile, 
  850.                                 this.selectedApplicationItemWrapped.file);
  851.           break;
  852.         case "defaultHandlerMenuItem":
  853.           prefs.setCharPref(PREF_SELECTED_READER, "client");
  854.           prefs.setComplexValue(PREF_SELECTED_APP, Ci.nsILocalFile, 
  855.                                 this.defaultSystemReaderItemWrapped.file);
  856.           break;
  857.         case "liveBookmarksMenuItem":
  858.           defaultHandler = "bookmarks";
  859.           prefs.setCharPref(PREF_SELECTED_READER, "bookmarks");
  860.           break;
  861.       }
  862.       var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
  863.                         getService(Ci.nsIFeedResultService);
  864.  
  865.       // Pull the title and subtitle out of the document
  866.       var feedTitle = this._document.getElementById(TITLE_ID).textContent;
  867.       var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
  868.       feedService.addToClientReader(this._window.location.href,
  869.                                     feedTitle, feedSubtitle);
  870.     }
  871.  
  872.     // If "Always use..." is checked, we should set PREF_SELECTED_ACTION
  873.     // to either "reader" (If a web reader or if an application is selected),
  874.     // or to "bookmarks" (if the live bookmarks option is selected).
  875.     // Otherwise, we should set it to "ask"
  876.     if (useAsDefault)
  877.       prefs.setCharPref(PREF_SELECTED_ACTION, defaultHandler);
  878.     else
  879.       prefs.setCharPref(PREF_SELECTED_ACTION, "ask");
  880.   },
  881.  
  882.   // nsIObserver
  883.   observe: function FW_observe(subject, topic, data) {
  884.     if (!this._window) {
  885.       // this._window is null unless this.write was called with a trusted
  886.       // window object.
  887.       return;
  888.     }
  889.  
  890.     if (topic == "nsPref:changed") {
  891.       switch (data) {
  892.         case PREF_SELECTED_READER:
  893.         case PREF_SELECTED_WEB:
  894.         case PREF_SELECTED_APP:
  895.           this._setSelectedHandler();
  896.           break;
  897.         case PREF_SELECTED_ACTION:
  898.           this._setAlwaysUseCheckedState();
  899.       }
  900.     } 
  901.   },
  902.  
  903.   /**
  904.    * Sets the icon for the given web-reader item in the readers menu
  905.    * if the favicon-service has the necessary icon stored.
  906.    * @param aURI
  907.    *        the reader URI.
  908.    * @param aMenuItem
  909.    *        the reader item in the readers menulist.
  910.    * @return true if the icon was set, false otherwise.
  911.    */
  912.   _setFaviconForWebReader:
  913.   function FW__setFaviconForWebReader(aURI, aMenuItem) {
  914.     var faviconsSvc = this._faviconService;
  915.     var faviconURL = null;
  916.     try {
  917.       faviconURL = faviconsSvc.getFaviconForPage(aURI);
  918.     }
  919.     catch(ex) { }
  920.  
  921.     if (faviconURL) {
  922.       var mimeType = { };
  923.       var bytes = faviconsSvc.getFaviconData(faviconURL, mimeType,
  924.                                              { /* dataLen */ });
  925.       if (bytes) {
  926.         var dataURI = "data:" + mimeType.value + ";" + "base64," +
  927.                       btoa(String.fromCharCode.apply(null, bytes));
  928.         aMenuItem.setAttribute("image", dataURI);
  929.         return true;
  930.       }
  931.     }
  932.  
  933.     return false;
  934.   },
  935.  
  936.    // nsINavHistoryService
  937.    onPageChanged: function FW_onPageChanged(aURI, aWhat, aValue) {
  938.      if (aWhat == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
  939.        // Go through the readers menu and look for the corresponding
  940.        // reader menu-item for the page if any.
  941.        var spec = aURI.spec;
  942.        var handlersMenulist = this._document.getElementById("handlersMenuList");
  943.        var possibleHandlers = handlersMenulist.firstChild.childNodes;
  944.        for (var i=0; i < possibleHandlers.length ; i++) {
  945.          if (possibleHandlers[i].getAttribute("webhandlerurl") == spec) {
  946.            this._setFaviconForWebReader(aURI, possibleHandlers[i]);
  947.            return;
  948.          }
  949.        }
  950.      }
  951.    },
  952.  
  953.    onBeginUpdateBatch: function() { },
  954.    onEndUpdateBatch: function() { },
  955.    onVisit: function() { },
  956.    onTitleChanged: function() { },
  957.    onDeleteURI: function() { },
  958.    onClearHistory: function() { },
  959.    onPageExpired: function() { },
  960.  
  961.   // nsIClassInfo
  962.   getInterfaces: function FW_getInterfaces(countRef) {
  963.     var interfaces = [Ci.nsIFeedWriter, Ci.nsIClassInfo, Ci.nsISupports];
  964.     countRef.value = interfaces.length;
  965.     return interfaces;
  966.   },
  967.   getHelperForLanguage: function FW_getHelperForLanguage(language) null,
  968.   contractID: "@mozilla.org/browser/feeds/result-writer;1",
  969.   classDescription: "Feed Writer",
  970.   classID: Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}"),
  971.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  972.   flags: Ci.nsIClassInfo.DOM_OBJECT,
  973.   _xpcom_categories: [{ category: "JavaScript global constructor",
  974.                         entry: "BrowserFeedWriter"}],
  975.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFeedWriter, Ci.nsIClassInfo,
  976.                                          Ci.nsIDOMEventListener, Ci.nsIObserver,
  977.                                          Ci.nsINavHistoryObserver])
  978. };
  979.  
  980. function NSGetModule(cm, file)
  981.   XPCOMUtils.generateModule([FeedWriter]);
  982.